<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>发布订阅模式</title>
    <style>
        dt {
            font-size: 18px;
        }

        dd {
            font-size: 16px;
            color: cornflowerblue;
            font-weight: bold;
        }

        pre {
            background: #e9ecef;
            color: #000;
            padding: 20px;
        }
    </style>
</head>

<body>
    <dl>
        <dt>发布订阅模式定义：</dt>
        <dd>是一种消息传递模式，消息的发布者（Publishers）一般将消息发布到特定消息中心，订阅者（Subscriber）可以按照自己的需求从消息中心订阅信息，跟消息队列挺类似的。</dd>
        <dd>在观察者模式只有两种组件：接收者和发布者，而发布-订阅模式中则有三种组件：发布者、消息中心和接收者。</dd>
    </dl>
    <dl>
        <dt>发布订阅模式的好处：</dt>
        <dd>1、可以解决一些异步进行的代码</dd>
        <dd>2、就算不解决异步问题，也可以使代码逻辑更清晰，更加具有结构化。</dd>

    </dl>
    <dl>
        <dt>适用场景：</dt>
        <dd>1、需要用户确定是否执行该操作，才能决定代码逻辑的下一步走向。</dd>
        <dd>2、为了使代码更加结构化，逻辑清晰，可以自定义事件，然后用类似于jquery中的$("main").trigger("eventName")的机制去触发。</dd>

    </dl>
    <pre>
        <code>
       
            class EventEmitter {
                constructor () {
                  this.eventChannel = {}; // 消息中心
                }
               
                // subscribe
                on (event, callback) {
                  this.eventChannel[event] ? this.eventChannel[event].push(callback) : this.eventChannel[event] = [callback]
                }
               
                // publish
                emit (event, ...args) {
                  this.eventChannel[event] && this.eventChannel[event].forEach(callback => callback(...args))
                }
               
                // remove event
                remove (event) {
                  if (this.eventChannel[event]) {
                    delete this.eventChannel[event]
                  }
                }
               
                // once event
                once (event, callback) {
                  this.on(event, (...args) => {
                    callback(...args);
                    this.remove(event)
                  })
                }
              }
    </code>
    </pre>
    <script>
        class EventEmitter {
            constructor() {
                this.eventChannel = {}; // 消息中心
            }

            // subscribe
            on(event, callback) {
                this.eventChannel[event] ? this.eventChannel[event].push(callback) : this.eventChannel[event] = [
                    callback
                ]
            }

            // publish
            emit(event, ...args) {
                this.eventChannel[event] && this.eventChannel[event].forEach(callback => callback(...args))
            }

            // remove event
            remove(event) {
                if (this.eventChannel[event]) {
                    delete this.eventChannel[event]
                }
            }

            // once event
            once(event, callback) {
                this.on(event, (...args) => {
                    callback(...args);
                    this.remove(event)
                })
            }
        }
        const event = new EventEmitter();
        event.on("err", console.log);
        event.on("data", data => {
            // do something
            console.log(data)
        });
        event.emit("err", "错误了哦")
        setTimeout(() => {
            event.emit("data", "成功了哦")

        }, 1000)
    </script>
</body>

</html>